package com.heo.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import com.heo.coder.packet.HeartbeatPacketRsp;
import com.heo.coder.packet.LoginPacketRsp;
import com.heo.coder.packet.QueryPacketRsp;
import com.heo.coder.packet.ActivePacketRsp;
import com.heo.coder.packet.ConfirmPacketRsp;
import com.heo.coder.packet.EndPacketRsp;
import com.heo.coder.packet.SocketPacket;
import com.heo.coder.packet.StartPacketRsp;
import com.heo.coder.packet.base.BasePacket;
import com.heo.thread.SendMessageSocketThread;
import com.heo.util.ByteHelper;
import com.heo.util.DeviceCommand;
import com.heo.util.NettyChannelMapUtils;
import com.heo.util.Utils;
import com.heo.pojo.ActiveInfoEntity;
import com.heo.pojo.ChargingInfoEntity;
import com.heo.pojo.DeviceInfoEntity;
import com.heo.pojo.DeviceModelEntity;
import com.heo.pojo.EndChargingInfoEntity;
import com.heo.pojo.OrgInfoEntity;
import com.heo.pub.ApplicationContextUtil;
import com.heo.pub.DictEnum;
import com.heo.service.DeviceInfoService;
import com.heo.service.DeviceModelService;
import com.heo.service.OrgInfoService;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyServerSocketHandler extends ChannelInboundHandlerAdapter {
	private Logger log = Logger.getLogger(this.getClass());
	private short sendSequenceId = 1;
	private String logHeader = "NettyServerSocketHandler";
	private String deviceImei = null;
	public ChannelHandlerContext mCtx;
	private BasePacket basePacket = null;
	private String appId = "";
	private String callUrl = "";
	// 初始化设备，并入库
    DeviceInfoService deviceInfoService = null;
    DeviceModelService deviceModelService = null;
    DeviceInfoEntity deviceInfo = null;
    private Map<String, String> flowNumber = new HashMap<String, String>();
//    boolean bool = false;

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		log.error(logHeader + "[断开]操作异常，socket连接已断开。");
		log.error(cause.getMessage(), cause);
		// 网络异常，断开。
		ctx.close();
	}

	/**
	 * 客户端连接上来的时候触发 连接上来的时候，并不知道客户端是谁，需要根据他后面发的指令才知道是谁。
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		this.mCtx = ctx;
		String channelId = ctx.channel().id().asShortText();
		String remoteIpAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
		logHeader = "[server][" + channelId + "][" + remoteIpAddress + "]";
		log.info(logHeader + "[连接]与客户端建立连接成功。");
	}

	/**
	 * 连接关闭成功后触发
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		String channelId = ctx.channel().id().asShortText();
		log.info(logHeader + "[断开]socket连接已断开。" + channelId);
	}

	/**
	 * 收到客户端传上来的数据触发
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		basePacket = (SocketPacket) msg;
//		log.info(basePacket);
		switch (basePacket.getControl()) {
		case DeviceCommand.LOGIN_COMMAND_REQ:
			// 初始化设备，并入库
		    deviceInfoService = (DeviceInfoService) ApplicationContextUtil.getBeanById("deviceInfoService");
		    deviceModelService = (DeviceModelService) ApplicationContextUtil.getBeanById("deviceModelService");
		    deviceImei = "000" + basePacket.getMacAddress();
		    deviceInfo = deviceInfoService.getDeviceInfoMapByImei(deviceImei);
		    if(deviceInfo == null) {
		    	throw new Exception(deviceImei + "设备不存在请联系管理员!!");
		    }
		    OrgInfoService orgInfoService  = (OrgInfoService) ApplicationContextUtil.getBeanById("orgInfoService");
		    OrgInfoEntity orgInfoEntity = orgInfoService.getByKey(deviceImei);
		    appId = orgInfoEntity.getAppId();
		    callUrl = orgInfoEntity.getCallUrl();
		    DeviceModelEntity deviceModel = deviceModelService.getDeviceModelMap(deviceInfo.getDeviceModelId());
			log.info("--imei--" +  deviceImei +"---- " + "登陆开始   控制码 = " + basePacket.getControl() + basePacket.toString());
			// 初始化设备，并入库
		    NettyChannelMapUtils.add(appId, deviceImei, deviceInfo.getDeviceChannelNum(), Utils.getShort(deviceModel.getDeviceModel()), this);
//			bool = true;
			LoginPacketRsp loginRsp = new LoginPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
					basePacket.getMsta(), DeviceCommand.LOGIN_COMMAND_RSP, "", "");
			loginRsp.setMsgContent();
			sendPacket(ctx.channel(), loginRsp);
			break;
		case DeviceCommand.HEARTBEAT_COMMAND_REQ:
			if(!Utils.checkNull(deviceImei)) {
				close();
			}
			log.info("--imei--" +  deviceImei +"---- " + "终端心跳  心跳码= " + basePacket.getControl() + "---" + basePacket.toString());
			NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
//			if(bool) {
//				bool = false;
//				StartPacketRsp startPacketRsp = new StartPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
//						basePacket.getMsta(), DeviceCommand.START_COMMAND_RSP, "", "");
//				startPacketRsp.setMsgContent(1, "13000000000", 1, 1, 1.25f, 2.50f, 1f);
//				SendMessageSocketThread startSocketThread = new SendMessageSocketThread(appId,
//						deviceImei, startPacketRsp, 8000);
//				NettyChannelMapUtils.sendTaskThreadPool.execute(startSocketThread);
//			}
			QueryPacketRsp queryPacketRsp = new QueryPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
					basePacket.getMsta(), DeviceCommand.QUERY_COMMAND_RSP, "", "");
			queryPacketRsp.setMsgContent(1);
			SendMessageSocketThread querySocketThread = new SendMessageSocketThread(appId,
					deviceImei, queryPacketRsp, 4000);
			NettyChannelMapUtils.sendTaskThreadPool.execute(querySocketThread);
			HeartbeatPacketRsp heartbeatRsp = new HeartbeatPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
					basePacket.getMsta(), DeviceCommand.HEARTBEAT_COMMAND_RSP, "", "");
			heartbeatRsp.setMsgContent();
			sendPacket(ctx.channel(), heartbeatRsp);
			break;
		case DeviceCommand.CONTROL_COMMAND_REQ:
			ChargingInfoEntity chargingnfoEntity = new ChargingInfoEntity(basePacket.getBody());
			log.info("--imei--" +  deviceImei +"---- " + "上报充电记录   上报= " + basePacket.getControl() + "status = " + chargingnfoEntity.getChargingStatus() + "， 值描叙  1 开始充电上传；2 结束充电上传；3 终端停电上传；4 充电过程实时上传");
			NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, 0, DictEnum.CallBackType.STATUS_REPORT, chargingnfoEntity.getChargingStatus(), callUrl);
			ConfirmPacketRsp confirmRsp = new ConfirmPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
					basePacket.getMsta(), DeviceCommand.CONFIRM_COMMAND_RSP, "", "");
			if(chargingnfoEntity.getChargingStatus() == 2 || chargingnfoEntity.getChargingStatus() == 3) {
				flowNumber.remove(deviceImei + chargingnfoEntity.getPassageway());
			}
			confirmRsp.setMsgContent(chargingnfoEntity.getPassageway(), chargingnfoEntity.getBatch());
			sendPacket(ctx.channel(), confirmRsp);
			NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
			break;
		case DeviceCommand.START_COMMAND_REQ:
			log.info("--imei--" +  deviceImei +"---- " + "下发充电 上报= " + basePacket.getControl() + "---" + basePacket.toString());
			ActiveInfoEntity activeInfoEntity = new ActiveInfoEntity(basePacket.getBody());
			log.info("status = " + activeInfoEntity.getStatus() + "， 值描叙  1可以充电 2不能充电3请求充电（充电桩发起）");
			NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
			break;
		case DeviceCommand.ERROR_COMMAND_REQ:
			log.info("--imei--" +  deviceImei +"---- " + "告警上报= " + basePacket.getControl() + "---" + basePacket.toString());
//			ActiveInfoEntity activeInfoEntity = new ActiveInfoEntity(basePacket.getBody());
//			log.info("status = " + activeInfoEntity.getStatus() + "， 值描叙  1可以充电 2不能充电3请求充电（充电桩发起）");
			NettyChannelMapUtils.refreshDeviceControlInfoExpire(appId, deviceImei);// 设备有消息上来，在redis里做延期
			break;
		case DeviceCommand.END_COMMAND_REQ:
			EndChargingInfoEntity endChargingInfoEntity = new EndChargingInfoEntity(basePacket.getBody());
			log.info("--imei--" +  deviceImei +"---- " + "结束充电记录   上报= " + basePacket.getControl() + "status = " + endChargingInfoEntity.getChargingStatus() + "， 值描叙  1,结束成功；2 结束失败；3主动结束成功上传");
			if(endChargingInfoEntity.getChargingStatus() == 1 || endChargingInfoEntity.getChargingStatus() == 3) {
				flowNumber.remove(deviceImei + endChargingInfoEntity.getPassageway());
				NettyChannelMapUtils.callBackSendStatus(appId, deviceImei, 0, DictEnum.CallBackType.STATUS_REPORT, 2, callUrl);
			}
			break;
		default:
			break;
		}
	}

	/**
	 * 发送数据包 当没办法提交给队列时使用
	 * 
	 * @param ctx
	 * @param packet
	 */
	public synchronized void sendPacket(Channel channel, BasePacket packet) {
		channel.writeAndFlush(packet).addListener(new ChannelFutureListener() {
			// 数据发送完毕后，触发本事件
			@Override
			public void operationComplete(ChannelFuture future) throws Exception {
				log.debug(logHeader + packet.getMacAddress() + "[应答]向设备<" + packet.getControl()
				+ ">发送数据成功， 发送内容：" + packet.toString());
			}
		});
	}
	
	/**
	 * 发送消息
	 * 
	 * @param rspJsonMap
	 * @throws Exception
	 */
	public synchronized void sendMessage(BasePacket packet)
			throws Exception {
		if (packet != null && mCtx != null) {
			mCtx.writeAndFlush(packet).addListener(new ChannelFutureListener() {
				// 数据发送完毕后，触发本事件
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					log.debug(logHeader + packet.getMacAddress() + "[应答]向设备<" + packet.getControl()
							+ ">发送数据成功， 发送内容：" + packet.toString());
				}
			});
		}
	}
	
	public synchronized void startCharging(int passageway, int sett, int voltage, float chargePower, float chargeAmount, float fee) throws Exception {
		log.info("startCharging" + basePacket.getControl() + "向设备<" + basePacket.getMacAddress() + ">发送数据成功， 发送开始");
		StartPacketRsp startPacketRsp = new StartPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
				basePacket.getMsta(), DeviceCommand.START_COMMAND_RSP, "", "");
		String flowNumberStr = startPacketRsp.setMsgContent(passageway, "13000000000", sett, voltage, chargePower, chargeAmount, fee);
		flowNumber.put(deviceImei + passageway, flowNumberStr);
		sendMessage(startPacketRsp);
	}
	
	public synchronized void endCharging(int passageway) throws Exception {
		log.info("endCharging" + basePacket.getControl() + "向设备<" + basePacket.getMacAddress() + ">发送数据成功， 发送结束");
		EndPacketRsp endPacketRsp = new EndPacketRsp(basePacket.getHead(), basePacket.getId(),  0, 
				basePacket.getMsta(), DeviceCommand.END_COMMAND_RSP, "", "");
		String flowNumberStr = flowNumber.get(deviceImei + passageway);
		if(!Utils.checkNull(flowNumberStr)) {
			flowNumberStr = "00000000";
		}
		endPacketRsp.setMsgContent(passageway, flowNumberStr, "13000000000");
		sendMessage(endPacketRsp);
	}
	
	public void close() throws IOException {
		if (mCtx != null) {
			NettyChannelMapUtils.remove(appId, deviceImei);
			mCtx.close();
		}
	}
	
	private String getSendSequenceIdHex() {
		if(sendSequenceId == 65535) {
			sendSequenceId = 1;
		}
		sendSequenceId++;
		return ByteHelper.shortToHexString(sendSequenceId);
	}
}
